use std::collections::{HashSet};
use sqlite_wasm_rs::sqlite3_stmt;
use crate::db::dao::im_config_dao::{config_inserts, config_query_by_names, config_updates};
use crate::db::db::{Db, DbParamType};
use crate::db::db_transaction::{begin, commit, transaction};
use crate::db::pojo::pojo::{ImConfig, ImConfigStatus};
use crate::db::util::{db_get_int64, db_get_string};
use crate::util::time::{parse_to_time};


pub fn save_configs(current_user_id: String, configs: &mut Vec<ImConfig>) {
    let db = Db::open();
    transaction(db.clone(), move |db: Db| {
        save_configs_business(db, current_user_id, configs);
    });
    // save_configs_business(db.clone(), current_user_id, configs)
}

fn save_configs_business(db: Db, current_user_id: String, configs: &mut Vec<ImConfig>) {
    let names = configs.iter().map(|c| &c.name).collect::<Vec<&String>>();
    let db_configs = config_query_by_names(db.clone(), &current_user_id, names);
    let db_ids = db_configs.iter().map(|config| &config.name).collect::<HashSet<&String>>();
    let mut insert_configs= Vec::new();
    let mut update_configs = Vec::new();
    for config in configs {
        let name = &config.name;
        if !db_ids.contains(name) {
            insert_configs.push(config);
        } else {
            update_configs.push(config);
        }
    }

    config_inserts(db.clone(), insert_configs);
    config_updates(db.clone(), update_configs);
}

pub fn save_configs2(configs: &mut Vec<ImConfig>) {
    log::info!("save_configs, size: {}", configs.len());
    let count = count_total_config();
    if count > 0 {
        log::info!("configs has data, count: {}", count);
        // return;
    }

    let db = Db::open();
    let sql = c"insert into im_config(currentUserId, name, content, createTime, updateTime) values(?, ?, ?, ?, ?)";

    begin(db.db());

    let mut params = vec![];
    for config in configs.iter() {
        let mut param = vec![];
        param.push(DbParamType::Str(&*config.current_user_id));
        param.push(DbParamType::Str(&*config.name));
        param.push(DbParamType::Str(&*config.content));
        param.push(DbParamType::DateTime2(&config.create_time));
        param.push(DbParamType::DateTime2(&config.update_time));
        params.push(param);
    }

    log::info!("db params, {:?}", params);
    let results = db.inserts("save config", sql, params);
    for (idx, config) in configs.iter_mut().enumerate() {
        let id = results.get(idx);
        if let Some(id) = id {
            config.id = Some(*id);
        }
    }

    commit(db.db());
}

pub fn count_total_config()-> usize {
    let db = Db::open();
    let size = db.count("config total",c"select count(*) from im_config", Vec::new());
    log::info!("db count: {}", size);
    size
}

pub fn query_configs()-> Vec<ImConfig> {
    let db = Db::open();

    let sql = c"SELECT id, currentUserId, name, content, createTime, updateTime FROM im_config";
    db.query("config query", sql, Vec::new(), Box::new(config_wrap))
}

pub fn clean_configs() {
    let db = Db::open();
    db.clean_db("im_config");
    drop(db);
}

pub fn create_index_for_configs() {
    let db = Db::open();
    db.exec("config create index","create unique index idx_im_config_cuid_name on im_config(currentUserId, name);");
    drop(db);
}

pub fn drop_index_for_configs() {
    let db = Db::open();
    db.exec("config drop index","drop index idx_im_config_cuid_name;");
    drop(db);
}

fn config_wrap(stmt: *mut sqlite3_stmt)-> ImConfig {
    unsafe  {
        let id = db_get_int64(stmt, 0);
        let current_user_id = db_get_string(stmt, 1).unwrap();
        let name = db_get_string(stmt, 2).unwrap();
        let content = db_get_string(stmt, 3).unwrap();
        let create_time_str = db_get_string(stmt, 4).unwrap();
        let update_time_str = db_get_string(stmt, 5).unwrap();
        ImConfig{
            id: Some(id),
            name,
            current_user_id,
            content,
            status: ImConfigStatus::Enable,
            create_time: parse_to_time(create_time_str),
            update_time: parse_to_time(update_time_str),
        }
    }
}